﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using WindowsFormsApplication1;
using System.Drawing;

namespace Controller
{  
    /*
     * Log luu tru cac thong tin do va tinh toan trong qua trinh kiem tra 
     */

    public class Log
    {
        DateTime timeRecord = new DateTime();
        int insulinDose = 0;
        float bloodLevel = 0;
        
        public DateTime TimeRecord
        {
            get { return timeRecord; }
            set { timeRecord = value; }
        }

        public int InsulinDose
        {
            get { return insulinDose; }
            set { insulinDose = value; }
        }


        public float BloodLevel
        {
            get { return bloodLevel; }
            set { bloodLevel = value; }
        }

        public Log(DateTime timeRecord, int insulinDose, float bloodLevel)
        {
            this.timeRecord = timeRecord;
            this.insulinDose = insulinDose;
            this.bloodLevel = bloodLevel;
        }
    }

    /*
     * PumpController thuc hien kiem tra luong duong va Tester
     * tinh toan va bom insuln vao nguoi su dung.
     */

    public class PumpController
    {
        //khai bao cac hang so hoat dong va trang thai
        public const int NON_OPERATION = 0;
        public const int MEASURE_BLOOD_LEVEL = 1;
        public const int PUMP_INSULIN = 2;
        public const int NORMAL = 3;
        public const int WARNING = 4;
        public const int ERROR = 5;

        // dinh dang hoat dong hien thoi va thoi gian toi da cho phep, cung thoi gian bat dau
        public int operativeCurrent = 0;
        public int maximumTime = 0;
        public DateTime startTimeOperation = new DateTime();

        public int lifeCircleTime = 0; // chu ki xoay vong kiem tra
        public Thread runAction;// luong thuc thi kiem tra

        // cac doi duong lien ket ngoai
        Tester tester;
        MainInterface mainInterfaceLinker;

        bool isNeedle = false; // doi so kiem tra kim tiem
        bool availableInsulinResorvoir = false; // doi so kiem tra binh chua Insulin
        int remainInsulin = 0; // doi so luu tru luong insulin con lai trong binh
        int safeMax = -1; // nguong lon nhat trong vung insulin an toan
        int safeMin = -1; // nguong thap nhap trong vung insulin an toan
        int maxDose = 5; // so luong insulin toi da trong mot lan bom
        int minDose = 1; // so luong insulin toi thieu trong mot lan bom
        int maxDailyDose = 0; //luong insulin toi da co the bom trong chu ki tich luy
        int cumulativeDose = 0; // luong insulin da bom trong chuy ki tich luy

        List<Log> listOfLogRecords = new List<Log>();// danh sach cac chi so da thu thap duoc
     
        public PumpController(Tester testerObj, ref MainInterface mainInterface)
        {
            this.tester = testerObj;
            this.mainInterfaceLinker = mainInterface;
            Initialization();
            runAction = new Thread(RunThread);
        }

        public PumpController()
        {
            Initialization();
            runAction = new Thread(RunThread);
        }

        ~PumpController()
        {
            if (runAction.IsAlive)
                runAction.Abort();
        }

        public void ResetSystem()
        {
            listOfLogRecords.Clear();
            ResetCumulativeDose();
            mainInterfaceLinker.buttonOnAndOff_Click(null, null);
            mainInterfaceLinker.resetBtn.Enabled = true;
            mainInterfaceLinker.resetButtonClicked = false;
        }

        public void RunThread()
        {
            while (true)
            {
                Run();
            }

        }

        public  Thread GetThread()
        {
            return  runAction;
        }

        public void ResetThread()
        {
            runAction = new Thread(RunThread);
        }

        public void Run()
        {
            if (File.Exists("lifeCirclePump.param"))
            {
                lifeCircleTime = Convert.ToInt16(File.ReadAllText("lifeCirclePump.param"));
                File.Delete("lifeCirclePump.param");
            }
            if (mainInterfaceLinker.resetButtonClicked)
            {
                 ResetSystem();
                 return;
            }
            else if (mainInterfaceLinker.flagTurnOn && mainInterfaceLinker.modeBtn.Text.Equals("Auto"))
            {
                if (!checkNeedle() || !checkInsulinReservoir())
                {
                    operativeCurrent = ERROR; // bao cho Tester module biet he thong dang bi loi

                    if(!isNeedle)
                        mainInterfaceLinker.addNotify("Needle assembly removed", ERROR);
                    else
                    {
                        mainInterfaceLinker.addNotify("Insulin reservoir removed", ERROR);
                    }
                    Thread.Sleep(2000); 

                    mainInterfaceLinker.buttonOnAndOff_Click(null, null);
                    Thread.Sleep(1000); 
                    return;
                 }

                startTimeOperation = DateTime.Now;
                operativeCurrent = MEASURE_BLOOD_LEVEL;
                maximumTime = 10; // seconds unit
                float bloodLevel = MeasureBlood();
                int insulinDose = ComputeInsulinDose(bloodLevel);

                if (insulinDose > 0)
                {
                    startTimeOperation = DateTime.Now;
                    operativeCurrent = PUMP_INSULIN;
                    maximumTime = 3;

                    if (!checkNeedle() || !checkInsulinReservoir())
                    {
                        operativeCurrent = ERROR;
                        if (!isNeedle)
                            mainInterfaceLinker.addNotify("Needle assembly removed", ERROR);
                        else
                        {
                            mainInterfaceLinker.addNotify("Insulin reservoir removed", ERROR);
                        }
                        insulinDose = 0;
                    }
                    else if( !DeliveryInsulin(insulinDose))
                       insulinDose = 0;
                }

                if (remainInsulin <= 10)
                    mainInterfaceLinker.addNotify("Low Insulin Remain", WARNING);
              
                Log newLog = new Log(DateTime.Now, insulinDose, bloodLevel);
                listOfLogRecords.Add(newLog);
                mainInterfaceLinker.UpdateScreen(remainInsulin, insulinDose, cumulativeDose, bloodLevel);

                startTimeOperation = DateTime.Now;
                maximumTime = lifeCircleTime;
                operativeCurrent = NON_OPERATION;
                ResetCumulativeDose();
                CheckTester();

                startTimeOperation = DateTime.Now;
                maximumTime = lifeCircleTime;
                Thread.Sleep(lifeCircleTime);
            }
            else
            {
                if (!checkNeedle() || !checkInsulinReservoir())
                {
                    operativeCurrent = ERROR;
                    listOfLogRecords.Clear();
                    Thread oldThread = runAction;
                    runAction = new Thread(RunThread);
                    mainInterfaceLinker.AddPumpControllerThead(ref runAction);
                    oldThread.Abort();
                    return;
                 }
                if (!mainInterfaceLinker.flagTurnOn && mainInterfaceLinker.resetBtn.Enabled)
                {
                    listOfLogRecords.Clear();
                    Thread oldThread = runAction;
                    runAction = new Thread(RunThread);
                    mainInterfaceLinker.AddPumpControllerThead(ref runAction);
                    oldThread.Abort();
                    return;
                }
                else if (!mainInterfaceLinker.resetBtn.Enabled)
                {
                    mainInterfaceLinker.resetButtonClicked = true;
                    mainInterfaceLinker.resetBtn.Enabled = true;
                    return;
                }
               

                // Khi che do manual dc bat
                //
                while(mainInterfaceLinker.flagTurnOn && mainInterfaceLinker.modeBtn.Text.Equals("Manual"));

                int insulinDose = Convert.ToInt16(mainInterfaceLinker.insulinDoseOption.Text);
                startTimeOperation = DateTime.Now;
                operativeCurrent = PUMP_INSULIN;
                maximumTime = 3;

                if (!checkNeedle() || !checkInsulinReservoir())
                {
                    operativeCurrent = ERROR;
                    if (!isNeedle)
                        mainInterfaceLinker.addNotify("Needle assembly removed", ERROR);
                    else
                    {
                        mainInterfaceLinker.addNotify("Insulin reservoir removed", ERROR);
                    }
                    insulinDose = 0;
                }

                DeliveryInsulin(insulinDose);
                if (remainInsulin <= 10)
                    mainInterfaceLinker.addNotify("Low Insulin Remain", WARNING);
                operativeCurrent = NON_OPERATION;
                maximumTime = lifeCircleTime + 500;
                Thread.Sleep(lifeCircleTime);
            }
        }

        void Initialization()
        {
            String txtContent = File.ReadAllText("pump.cfg");
            String[] subConnent = txtContent.Split('\n');
            remainInsulin = Convert.ToInt16( subConnent[0].Split('\t')[0] );
            mainInterfaceLinker.remainInsulinBar.Value = remainInsulin;
            safeMax = Convert.ToInt16(subConnent[1].Split('\t')[0]);
            safeMin = Convert.ToInt16(subConnent[2].Split('\t')[0]);
            maxDailyDose = Convert.ToInt16(subConnent[3].Split('\t')[0]);
            lifeCircleTime = Convert.ToInt16(subConnent[4].Split('\t')[0]);
            isNeedle = Convert.ToBoolean(subConnent[5].Split('\t')[0]);
            availableInsulinResorvoir = Convert.ToBoolean(subConnent[6].Split('\t')[0]);
        }

        public bool DeliveryInsulin(int insulinDose)
        {
            if (!checkNeedle() || !checkInsulinReservoir())
            {
                operativeCurrent = ERROR;
                if (!isNeedle)
                    mainInterfaceLinker.addNotify("Needle assembly removed", ERROR);
                else
                {
                    mainInterfaceLinker.addNotify("Insulin reservoir removed", ERROR);
                }
                operativeCurrent = ERROR;
                return false;
            }

            //Delivery Pump Fail Tester
            //
            if(File.Exists("deliveryFail.param"))
            {
                File.Delete("deliveryFail.param");
                while (true);
            }

            if (File.Exists("remainInsulin.param"))
            {
                remainInsulin = Convert.ToInt16(File.ReadAllText("remainInsulin.param"));
                File.Delete("remainInsulin.param");
            }

            if (remainInsulin >= insulinDose)
            {
                remainInsulin -= insulinDose;
                cumulativeDose += insulinDose;
            }
            else
            {
                mainInterfaceLinker.addNotify("Low Insulin Remain",WARNING);
                return false;
            }
            return true;
        }

        private void ResetCumulativeDose()
        {
            int rate = lifeCircleTime / 10;
            if (listOfLogRecords.Count % 10 == 0)
            {
                cumulativeDose = 0;
                mainInterfaceLinker.addNotify("", NORMAL);
            }
        }

        private bool SetDose(int insulinDose)
        {
            return DeliveryInsulin(insulinDose);
        }

        private int ComputeInsulinDose(float bloodLevel)
        {
            float insulinDose = 0;
            if (bloodLevel < safeMin)
            {
               mainInterfaceLinker.addNotify("Low Sugar", WARNING);
               mainInterfaceLinker.r0BloodLevel.ForeColor = Color.Yellow;
               return 0;
            }
            else if (bloodLevel >= safeMin && bloodLevel <= safeMax)
            {
                mainInterfaceLinker.r0BloodLevel.ForeColor = Color.Blue;
                int noOfRecored = listOfLogRecords.Count;
                if (noOfRecored >= 2)
                {
                    if (((bloodLevel - listOfLogRecords[noOfRecored - 1].BloodLevel) > 0) && ((listOfLogRecords[noOfRecored-1].BloodLevel - listOfLogRecords[noOfRecored - 2].BloodLevel) > 0))
                    {
                        insulinDose = (bloodLevel - listOfLogRecords[noOfRecored - 1].BloodLevel) / 4;
                    }
                    else
                        return 0;
                }
                else
                    insulinDose = (bloodLevel/4 );
            }
            else
            {
                mainInterfaceLinker.addNotify("High Sugar", WARNING);
                mainInterfaceLinker.r0BloodLevel.ForeColor = Color.Red;
                int noOfRecored = listOfLogRecords.Count;
                if (noOfRecored >= 2)
                {
                    if (!(((bloodLevel - listOfLogRecords[noOfRecored - 1].BloodLevel) < 0) &&( (listOfLogRecords[noOfRecored - 1].BloodLevel - listOfLogRecords[noOfRecored - 2].BloodLevel) < 0)))
                    {
                        insulinDose = (bloodLevel - listOfLogRecords[noOfRecored - 1].BloodLevel) / 4;
                    }
                    else
                        return 0;
                }
                else
                    insulinDose = (bloodLevel/4);
            }

            if ((cumulativeDose + insulinDose) > maxDailyDose)
            {
                mainInterfaceLinker.addNotify("Exceed Max Daily Dose",WARNING);
                insulinDose = (cumulativeDose + insulinDose) - maxDailyDose;
            }
            else if (insulinDose > maxDose)
            {
                mainInterfaceLinker.addNotify("Exceed Max Dose", WARNING);
                insulinDose = maxDose;
            }
            else if (insulinDose < minDose)
            {
                insulinDose = minDose;
            }
            return (int)insulinDose;
        }

        private float MeasureBlood()
        {
            float resultMeasure = 0;
            if (! File.Exists("bloodLevel.param"))
            {
                Random rdBloodLevel = new Random();
                resultMeasure = (float)((rdBloodLevel.Next(1,34)) + Math.Round(rdBloodLevel.NextDouble(), 1));
            }
            else
            {
                String txtContent = File.ReadAllText("bloodLevel.param");
                resultMeasure = (float)Convert.ToDouble(txtContent);
                File.Delete("bloodLevel.param");
                if (resultMeasure < 1 || resultMeasure > 35)
                    while (true) ;
            }
            return resultMeasure;
        }

        private void CheckTester()
        {
            if (!tester.threadAction.IsAlive)
                return;

            DateTime currentTime = DateTime.Now;
            int noHour = currentTime.Hour - tester.timeBegin.Hour;
            int noMinute = currentTime.Minute - tester.timeBegin.Minute;
            int noSecond = currentTime.Second - tester.timeBegin.Second;

            if ((noHour * 3600 + noMinute * 60 + noSecond) > tester.maxTime)
            {
                tester.threadAction.Abort();
                tester.resetThread();
         
                Thread testerThread = tester.GetThread();
                mainInterfaceLinker.AddTesterThread(ref testerThread);
                mainInterfaceLinker.resetBtn_Click(null, null);
                Thread.Sleep(2000);
            }
        }

        private bool checkNeedle()
        {
            // Needle Remove Tester
            //
            if (File.Exists("needle.param"))
            {
                isNeedle = Convert.ToBoolean(File.ReadAllText("needle.param"));
                File.Delete("needle.param");
            }
            return isNeedle;
        }

        private bool checkInsulinReservoir()
        {
            // Insulin Reservoir Removed Tester
            //
            if (File.Exists("insulinResevoir.param"))
            {
                availableInsulinResorvoir = Convert.ToBoolean(File.ReadAllText("insulinResevoir.param"));
                File.Delete("insulinResevoir.param");
            }
            return availableInsulinResorvoir;
        }
    }

    /*
     * Tester kiem tra tinh trang hoat dong cua PumpController
     * 
     */

    public class Tester
    {
        public DateTime timeBegin;// thoi gian bat dau kiem tra
        public int maxTime = 0; // thoi gian toi da cho phep kiem tra va xoay vong

        //cac lien ket ngoai
        PumpController pumpController;
        MainInterface mainInterfaceLinker;

        public Thread threadAction;// luong thuc hien viec kiem tra

        //can hang so trang thai cua he thong
        private const int NORMAL = 3;
        private const int WARNING = 4;
        private const int ERROR = 5;

        public Tester(ref MainInterface mainInterfaceObj)
        {
            this.mainInterfaceLinker = mainInterfaceObj;
            threadAction = new Thread(RunThread);
            timeBegin = DateTime.Now;

            string content = File.ReadAllText("tester.cfg");
            string[] subContent = content.Split('\n');
            maxTime = Convert.ToInt16(subContent[0].Split('\t')[0]);
        }
       
        public void CreateLinkerPumpController(ref PumpController pumpCotrollerObj)
        {
            this.pumpController = pumpCotrollerObj;
        }

        public void RunThread()
        {
            while (true)
            {
                Run();
            }
        }

        public Thread GetThread()
        {
            return threadAction;
        }

        public void resetThread()
        {
            threadAction = new Thread(RunThread);
        }

        public void Run()
        {

            if (File.Exists("lifeCircleTester.param"))
            {
                maxTime = Convert.ToInt16(File.ReadAllText("lifeCircleTester.param"));
                File.Delete("lifeCircleTester.param");
            }
            timeBegin = DateTime.Now;

            if (pumpController.operativeCurrent == ERROR)
            {
                Thread oldThread = threadAction;
                threadAction = new Thread(RunThread);
                mainInterfaceLinker.AddTesterThread(ref threadAction);
                oldThread.Abort();
                return;
            }

            if (mainInterfaceLinker.resetButtonClicked)
            {
                return;
            }

            if (!mainInterfaceLinker.flagTurnOn)
            {
                Thread oldThread = threadAction;
                threadAction = new Thread(RunThread);
                mainInterfaceLinker.AddTesterThread(ref threadAction);
                oldThread.Abort();
                return;
            }

            if (File.Exists("testerFail.param"))
            {
                File.Delete("testerFail.param");
                while (true);
            }

            CheckController();
            CheckPin();
            timeBegin = DateTime.Now;
            Thread.Sleep(maxTime*1000-500);

        }
        private void CheckController()
        {
            if (mainInterfaceLinker.modeBtn.Text.Equals("Manual"))
                return;

            DateTime currentTime = DateTime.Now;
            int noHour = currentTime.Hour - pumpController.startTimeOperation.Hour;
            int noMinute = currentTime.Minute - pumpController.startTimeOperation.Minute;
            int noSecond = currentTime.Second - pumpController.startTimeOperation.Second;

            if ((noHour * 3600 + noMinute * 60 + noSecond) > pumpController.maximumTime)
            {
                ResetSystem();
            }
        }
      
        private void ResetSystem()
        {
            pumpController.runAction.Abort();
            pumpController.ResetThread();
         
            mainInterfaceLinker.AddPumpControllerThead(ref pumpController.runAction);
            mainInterfaceLinker.buttonOnAndOff_Click(null, null);
            Thread.Sleep(2000);

            Thread oldThread = threadAction;
            threadAction = new Thread(RunThread);
            mainInterfaceLinker.AddTesterThread(ref threadAction);
            mainInterfaceLinker.buttonOnAndOff_Click(null, null);
            oldThread.Abort();
        }
      
        private int CheckPin()
        {
            int remainPIN = 0;

            // Pin Tester
            //
            if (File.Exists("PIN.param"))
            {
                String txtContent = File.ReadAllText("PIN.param");
                remainPIN = Convert.ToInt16(txtContent);
                mainInterfaceLinker.batteryInfoLbl.Text = remainPIN.ToString() + "%";
                mainInterfaceLinker.batteryBar.Value = remainPIN;
                File.Delete("PIN.param");
            }
            else
            {
                if (mainInterfaceLinker.batteryInfoLbl.Text.Equals(""))
                    return remainPIN;
                remainPIN = Convert.ToInt16(mainInterfaceLinker.batteryInfoLbl.Text.Split('%')[0]);

            }
            if (remainPIN <= 10)
            {
                mainInterfaceLinker.batteryInfoLbl.ForeColor = Color.Red;
                mainInterfaceLinker.addNotify("Low Battery", WARNING);
            }
            else
                mainInterfaceLinker.batteryInfoLbl.ForeColor = Color.Black;

            return remainPIN;
        }
    }
}